// Hash table
// An array and linked list
#include "list.h"
#include "log.h"
#include <stdlib.h>

typedef int (*HashFunc)(void *);
typedef struct _hash_table
{
    List **bucket;           // bucket, List array
    int bucket_size;
    HashFunc hash_func;
}HashTable;

HashTable *hashtable_new(int bucket_size, HashFunc hash_func)
{
    int i = 0;
    HashTable *ht = (HashTable *)malloc(sizeof(HashTable));
    if (!ht)
    {
        log_error("malloc for hashtable fail");
        return NULL;
    }

    if (bucket_size <= 0 || !hash_func)
    {
        return NULL;
    }
    ht->bucket = (List **)malloc(bucket_size * sizeof(List *));
    if (ht->bucket == NULL)
    {
        log_error("malloc for buckets fail");
        return NULL;
    }

    for (i = 0; i < bucket_size; i++)
    {
        ht->bucket[i] = list_new();
    }
    ht->bucket_size = bucket_size;
    ht->hash_func = hash_func;

    return ht;
}

void hashtable_free(HashTable *ht)
{
    if (!ht) return;

    for (int i = 0; i < ht->bucket_size; i++)
    {
        list_free(ht->bucket[i]);
    }

    free(ht);
}

HashTable *hashtable_insert(HashTable *self, void *data)
{
    int idx = -1;
    if (!self || !data)
    {
        return NULL;
    }
    idx = self->hash_func(data) % self->bucket_size;
    list_insert_tail(self->bucket[idx], data);
    return self;
}


static void int_print(void *data)
{
    int *p = (int *)data;
    printf("%d ", *p);
}
void hashtable_print(HashTable *ht)
{
    if (!ht) return;

    int i = 0;
    for (i = 0; i < ht->bucket_size; i++)
    {
        printf("bucket%d: ", i);
        list_print(ht->bucket[i], int_print);
        printf("\n");
    }
}

int hash_int(void *data)
{
    int *p = data;
    return *p;
}

// #[test]
void hashtable_test(void)
{
    HashTable *ht = hashtable_new(10, hash_int);
    int a = 0;
    int b = 1;
    int c = 10;
    hashtable_insert(ht, &a);
    hashtable_insert(ht, &b);
    hashtable_insert(ht, &c);
    hashtable_print(ht);
    hashtable_free(ht);
}